package com.cardone.platform.authority.service;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;

import com.cardone.common.dto.PaginationDto;
import com.cardone.common.utils.EntityUtils;
import com.cardone.context.function.Function2;
import com.cardone.persistent.CommonDtoAttributes;
import com.cardone.platform.authority.dao.NavigationDao;
import com.cardone.platform.authority.dto.NavigationDto;
import com.cardone.platform.common.utils.EntityTreeUtils;
import com.cardone.platform.configuration.utils.DictionaryUtils;
import com.cardone.platform.configuration.utils.SiteUtils;

/**
 * 导航服务
 * 
 * @author yaoht
 * 
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class NavigationDefaultService implements NavigationService {

	/**
	 * 导航数据持久
	 */
	private NavigationDao navigationDao;

	@Override
	@Transactional
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public int deleteNavigationByCode(@NonNull final NavigationDto deleteNavigation) {
		return this.navigationDao.deleteByCode(deleteNavigation, new String[] {});
	}

	@Override
	@Transactional
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public int deleteNavigationByIds(@NonNull final NavigationDto deleteNavigation) {
		return this.navigationDao.deleteByIds(deleteNavigation);
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public List<NavigationDto> findListNavigationByCode(@NonNull final NavigationDto findListNavigation) {
		return this.navigationDao.findListByCode(NavigationDto.class, findListNavigation, new String[] {}, new String[] {});
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public List<NavigationDto> findListNavigationByName(@NonNull final NavigationDto findListNavigation) {
		return this.navigationDao.findListByName(NavigationDto.class, findListNavigation);
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public List<NavigationDto> findListNavigationByParentId(final String parentId) {
		return this.navigationDao.findListByParentId(NavigationDto.class, parentId);
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_2)
	public List<NavigationDto> findListNavigationByParentId(final String parentId, final int level) {
		final List<NavigationDto> navigationList = this.findListNavigationByParentId(parentId);

		if (CollectionUtils.isEmpty(navigationList)) {
			return navigationList;
		}

		if (level < 1) {
			return navigationList;
		}

		for (final NavigationDto navigationDto : navigationList) {
			navigationDto.setChilds(this.findListNavigationByParentId(navigationDto.getId(), (level - 1)));
		}

		return navigationList;
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public NavigationDto findNavigationByCode(@NonNull final NavigationDto findNavigation) {
		return this.navigationDao.findByCode(NavigationDto.class, findNavigation, null, CommonDtoAttributes.id.name());
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public NavigationDto findNavigationById(@NonNull final NavigationDto findNavigation) {
		return this.navigationDao.findByCode(NavigationDto.class, findNavigation, null, CommonDtoAttributes.id.name());
	}

	@Override
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	@Transactional
	public void initNavigation(final List<NavigationDto> initNavigationList) {
		if (CollectionUtils.isEmpty(initNavigationList)) {
			return;
		}

		for (final NavigationDto initNavigation : initNavigationList) {
			if (StringUtils.isBlank(initNavigation.getSiteId())) {
				final String siteId = SiteUtils.readSiteIdByCode(initNavigation.getSiteCode());

				if (StringUtils.isBlank(siteId)) {
					continue;
				}

				initNavigation.setSiteId(siteId);
			}

			if (StringUtils.isBlank(initNavigation.getTypeId())) {
				final String typeId = DictionaryUtils.readDictionaryIdByTypeCodeAndCode(StringUtils.EMPTY,
				        initNavigation.getTypeTypeCode(), initNavigation.getTypeCode());

				if (StringUtils.isBlank(typeId)) {
					continue;
				}

				initNavigation.setTypeId(typeId);
			}

			final NavigationDto oldNavigation = this.saveNavigationByCode(initNavigation);

			if (CollectionUtils.isEmpty(initNavigation.getChilds())) {
				continue;
			}

			for (final NavigationDto childNavigation : initNavigation.getChilds()) {
				childNavigation.setSiteId(oldNavigation.getSiteId());

				childNavigation.setTypeId(oldNavigation.getTypeId());

				childNavigation.setParentId(oldNavigation.getId());

				childNavigation.setTreeTypeCode(initNavigation.getTreeTypeCode());

				childNavigation.setTreeCode(initNavigation.getTreeCode());
			}

			this.initNavigation(initNavigation.getChilds());
		}
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public PaginationDto<NavigationDto> paginationNavigationByCode(@NonNull final NavigationDto paginationNavigation) {
		return this.navigationDao.paginationByCode(NavigationDto.class, paginationNavigation);
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public PaginationDto<Map<String, Object>> paginationNavigationByName(@NonNull final NavigationDto paginationNavigation) {
		return this.navigationDao.paginationByName(paginationNavigation);
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public Integer readNavigationByCodeNqId(@NonNull final NavigationDto readNavigation) {
		return this.navigationDao.readByCodeNqId(Integer.class, readNavigation);
	}

	@Override
	@Cacheable(value = NavigationService.BEAN_ID, key = com.cardone.common.cache.Constants.KEY_1)
	public Integer readNavigationByName(@NonNull final NavigationDto readNavigation) {
		return this.navigationDao.readByName(Integer.class, null, readNavigation);
	}

	@Override
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public void reload() {
		this.navigationDao.reload();
	}

	/**
	 * 保存：实体树
	 * 
	 * @param saveNavigation
	 *            导航对象
	 */
	private void saveEntityTree(final NavigationDto saveNavigation) {
		EntityTreeUtils.saveEntityTree(saveNavigation, new Function2<String, String, String>() {
			@Override
			public String execution(final String typeCo, final String code) {
				return DictionaryUtils.readDictionaryIdByTypeCodeAndCode(typeCo, code);
			}
		});
	}

	@Override
	@Transactional
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public NavigationDto saveNavigationByCode(@NonNull final NavigationDto saveNavigation) {
		final String[] whereAndEqProperties = EntityUtils.getWhereProperties(saveNavigation, NavigationDao.DtoAttributes.siteId.name(),
		        CommonDtoAttributes.typeId.name(), CommonDtoAttributes.code.name());

		final NavigationDto oldNavigation = this.navigationDao.findByCode(NavigationDto.class, saveNavigation, null,
		        whereAndEqProperties);

		if (oldNavigation == null) {
			saveNavigation.setId(UUID.randomUUID().toString());

			this.navigationDao.insertByCode(saveNavigation);

			this.saveEntityTree(saveNavigation);

			final String[] notNullIdProperties = EntityUtils.getNotNullIdProperties(saveNavigation);

			return this.navigationDao.findByCode(NavigationDto.class, saveNavigation, null, notNullIdProperties);
		}

		final String[] notNullIdProperties = EntityUtils.getNotNullIdProperties(oldNavigation);

		com.cardone.common.utils.BeanUtils.copyProperties(oldNavigation, saveNavigation, notNullIdProperties);

		this.saveEntityTree(saveNavigation);

		final String[] useProperties = EntityUtils.addNotNullTimeSegmentProperties(
		        new String[] { NavigationDao.DtoAttributes.siteId.name(), CommonDtoAttributes.parentId.name(),
		                CommonDtoAttributes.typeId.name(), CommonDtoAttributes.code.name(), CommonDtoAttributes.name.name(),
		                CommonDtoAttributes.orderCode.name(), CommonDtoAttributes.value.name(), CommonDtoAttributes.remark.name() },
		        saveNavigation);

		String[] updateProperties = com.cardone.common.utils.BeanUtils.diffProperties(saveNavigation, oldNavigation, useProperties);

		if (ArrayUtils.isEmpty(updateProperties)) {
			return oldNavigation;
		}

		updateProperties = ArrayUtils.removeElement(updateProperties, CommonDtoAttributes.parentId.name());

		if (ArrayUtils.isEmpty(updateProperties)) {
			return oldNavigation;
		}

		this.navigationDao.updateByCode(saveNavigation, updateProperties, notNullIdProperties);

		return this.navigationDao.findByCode(NavigationDto.class, oldNavigation, null, notNullIdProperties);
	}

	@Override
	@Transactional
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public NavigationDto saveNavigationByIdOrCode(@NonNull final NavigationDto saveNavigation) {
		final String[] whereAndEqProperties = EntityUtils.getWhereProperties(saveNavigation, CommonDtoAttributes.code.name());

		final NavigationDto oldNavigation = this.navigationDao.findByCode(NavigationDto.class, saveNavigation, null,
		        whereAndEqProperties);

		if (oldNavigation == null) {
			saveNavigation.setId(UUID.randomUUID().toString());

			this.navigationDao.insertByCode(saveNavigation);

			final String[] notNullIdProperties = EntityUtils.getNotNullIdProperties(saveNavigation);

			return this.navigationDao.findByCode(NavigationDto.class, saveNavigation, null, notNullIdProperties);
		}

		final String[] useProperties = EntityUtils.addNotNullTimeSegmentProperties(new String[] { CommonDtoAttributes.parentId.name(),
		        CommonDtoAttributes.typeId.name(), CommonDtoAttributes.code.name(), CommonDtoAttributes.name.name(),
		        CommonDtoAttributes.orderCode.name(), CommonDtoAttributes.value.name(), CommonDtoAttributes.remark.name() },
		        saveNavigation);

		final String[] updateProperties = com.cardone.common.utils.BeanUtils.diffProperties(saveNavigation, oldNavigation,
		        useProperties);

		if (ArrayUtils.isEmpty(updateProperties)) {
			return oldNavigation;
		}

		final String[] notNullIdProperties = EntityUtils.getNotNullIdProperties(oldNavigation);

		com.cardone.common.utils.BeanUtils.copyProperties(oldNavigation, saveNavigation, notNullIdProperties);

		this.navigationDao.updateByCode(saveNavigation, updateProperties, notNullIdProperties);

		return this.navigationDao.findByCode(NavigationDto.class, oldNavigation, null, notNullIdProperties);
	}

	@Override
	@Transactional
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public int updateNavigationByCode(@NonNull final NavigationDto updateNavigation) {
		return this.navigationDao.updateByCode(updateNavigation, new String[] {}, new String[] {});
	}

	@Override
	@Transactional
	@CacheEvict(value = NavigationService.BEAN_ID, allEntries = true)
	public int updateNavigationByIds(@NonNull final NavigationDto updateNavigation) {
		return this.navigationDao.updateByIds(updateNavigation);
	}
}